package com.jhs.boot.system.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jhs.boot.common.base.IBaseEnum;
import com.jhs.boot.common.beans.Common;
import com.jhs.boot.common.constant.SystemConstants;
import com.jhs.boot.common.enums.DataScopeEnum;
import com.jhs.boot.common.enums.NodesColorEnum;
import com.jhs.boot.common.enums.NodesEnum;
import com.jhs.boot.common.exception.BusinessException;
import com.jhs.boot.common.model.Option;
import com.jhs.boot.common.result.Result;
import com.jhs.boot.config.FeignClientFactory;
import com.jhs.boot.core.security.util.SecurityUtils;
import com.jhs.boot.system.converter.SysNodesConverter;
import com.jhs.boot.system.mapper.SysNodesMapper;
import com.jhs.boot.system.model.entity.Dept;
import com.jhs.boot.system.model.entity.SysNodeRoute;
import com.jhs.boot.system.model.entity.SysNodes;
import com.jhs.boot.system.model.entity.SysRoute;
import com.jhs.boot.system.model.form.SysNodeRouteForm;
import com.jhs.boot.system.model.form.SysNodesForm;
import com.jhs.boot.system.model.query.SysNodesQuery;
import com.jhs.boot.system.model.vo.*;
import com.jhs.boot.system.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 节点服务实现类
 *SET_IF_ABSENT    ---->  NX 仅当key不存在时，set才会生效
 *
 * SET_IF_PRESENT   ---->XX 仅当key存在时，set才会生效
 *
 * NX – Only set the key if it does not already exist.
 * XX – Only set the key if it already exist.
 * @author zxy
 * @since 2025-08-11 19:17
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysNodesServiceImpl extends ServiceImpl<SysNodesMapper, SysNodes> implements SysNodesService {
    private final StringRedisTemplate redisTemplate;
    private final StringRedisTemplate redisTemplateStr;
    private final RedisTemplate<String, Object> redisTemplateObj;
    private final SysNodesConverter sysNodesConverter;
    private final DeptService deptService;
    private final FeignClientFactory feignClientFactory;
    private final SysRouteService sysRouteService;
    private final SysNodeRouteService sysNodeRouteService;
    /**
    * 获取节点分页列表
    *
    * @param queryParams 查询参数
    * @return {@link IPage<SysNodesVO>} 节点分页列表
    */
    @Override
    public IPage<SysNodesVO> getSysNodesPage(SysNodesQuery queryParams) {
        Long deptId = SecurityUtils.getDeptId();
        //根据角色的权限来分配
//        List<DeptVO> deptListByParentId =null;
//        if (!SecurityUtils.isRoot()){
////            deptListByParentId=deptService.getDeptListByParentId(DeptQuery.builder().idwords(deptId).build());
//            Integer dataScope = SecurityUtils.getDataScope();
//        }
////        else{
////            deptListByParentId=deptService.getAllDept(Common.ZERO);
////        }
//        queryParams.setDeptIds( CollectionUtils.isNotEmpty(deptListByParentId) ?deptListByParentId.stream().map(DeptVO::getId).collect(Collectors.toList()):null);
        Page<SysNodesVO> pageVO = this.baseMapper.getSysNodesPage(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                queryParams
        );
        return pageVO;
    }
    
    /**
     * 获取节点表单数据
     *
     * @param id 节点ID
     * @return 节点表单数据
     */
    @Override
    public SysNodesForm getSysNodesFormData(Long id) {
        SysNodes entity = this.getById(id);
        return sysNodesConverter.toForm(entity);
    }
    
    /**
     * 新增节点
     *
     * @param formData 节点表单对象
     * @return 是否新增成功
     */
    @Override
    public boolean saveSysNodes(SysNodesForm formData) {
        SysNodes entity = sysNodesConverter.toEntity(formData);
        NodesEnum nodesEnum = NodesEnum.find(entity.getType());
        NodesColorEnum nodesColorEnum = NodesColorEnum.find(entity.getType());
        entity.setSymbolSize(nodesEnum.getVal());
        entity.setColor(nodesColorEnum.getColor_no());
        entity.setLevel(entity.getType());
        checkUpParam(entity);
        boolean save = this.save(entity);
        if (save){
            log.info("baocunhou {}",entity);
            getRedisIp(entity);
        }
        return save;
    }

    @Override
    public void loadDateToRedis(List<SysNodes> list) {
        list.stream().forEach(entity -> getRedisIp(entity));
    }



    /**
     * 替换最后一个“-”后的值
     * @param str 原始数据
     * @param separator 分隔符
     * @param replacement 替换的值
     * @return 返回替换后的值
     */
    public static String replaceAfterLast(String str, String separator, String replacement) {
        int lastIndex = str.lastIndexOf(separator);
        if (lastIndex == -1) {
            return str + separator + replacement;
        }
        return str.substring(0, lastIndex + separator.length()) + replacement;
    }

    /**
     * 将节点的状态跟新
     * @param one 父节点
     * @param status 子节点的状态
     */
    @Override
    public void updayeNodeAndRedis(SysNodes one,Integer status) {
        // 下线所有子终端
        updateTerminalNode(null,one,status);
       /* List<SysNodes> nodesList =  list(new LambdaQueryWrapper<SysNodes>()
                .gt(SysNodes::getType, Common.ONE)
                .eq(SysNodes::getDeptId, one.getDeptId())
                .eq(SysNodes::getParentId, one.getId())
                .eq(SysNodes::getStatus, status));
        nodesList.stream().forEach(entity -> {
            NodesEnum nodesEnum = NodesEnum.find(entity.getType());
            NodesColorEnum nodesColorEnum = NodesColorEnum.find(entity.getType());
            entity.setStatus(status==0?Common.ONE:Common.ZERO);
            entity.setSymbolSize(nodesEnum.getVal());
            entity.setColor(entity.getStatus()==0?nodesColorEnum.getColor_no():nodesColorEnum.getColor());
            entity.setUpdateTime(LocalDateTime.now());

            boolean updateById = updateById(entity);
            if (updateById){
                String ip = entity.getType() > 1 ? (entity.getType()>2?entity.getUserCode():entity.getMac()) : entity.getIp();
                String keyVal = Common.STATUS + entity.getType() + Common.HENG + ip + Common.HENG + "*";
                Set<String> keys = redisTemplate.keys(keyVal);
                keys.stream().forEach(s -> {
                    String newKey = Common.STATUS + entity.getType() + entity.getIp() + Common.HENG +entity.getStatus();
                    redisTemplateObj.delete(s);
                    redisTemplateObj.opsForValue().setIfAbsent( newKey,JSONUtil.toJsonStr(entity));
                });
            }
        });*/

//        this.update().set(SysNodes::getStatus,Common.ZERO)
//                .set(SysNodes::getColor, NodesColorEnum.find(Common.ZERO).getColor_no())
//                .set(SysNodes::getUpdateTime, LocalDateTime.now())
//                .gt(SysNodes::getType, Common.ONE)
//                .eq(SysNodes::getDeptId,one.getDeptId())
//                .eq(SysNodes::getParentId,one.getId())
//                .eq(SysNodes::getStatus,Common.ONE)
//                .update();
    }
    /**
     * 是否存在匹配模式的key
     * @param pattern
     * @return 返回模糊查询的key
     */
    public Set<String> getRedisKeyByPattern(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            return keys;
        }
        return null;
    }

    /**
     *
     * updateNextNode 的状态是 true 时我们需要跟更新次节点
     * updateNextNode 的状态是 false 时我们不需要跟更新次节点
     * @param one 更新 节点
     * @param updateNextNode  更新节点的子节点
     * @param statusNext  查询的子节点的状态
     */
    @Override
    public void upNoticeNodeAndRedisById(SysNodes one,Boolean updateNextNode,Integer statusNext) {
        //更新数据库
        one.setStatus(one.getStatus()==0?Common.ONE:Common.ZERO);
        one.setUpdateTime(LocalDateTime.now());
        NodesColorEnum nodesColorEnum = NodesColorEnum.find(one.getType());
        one.setColor(one.getStatus()==0?nodesColorEnum.getColor_no():nodesColorEnum.getColor());
        String oldKey = Common.STATUS + one.getType()+ Common.HENG  + one.getIp() + Common.HENG +"*";
        String newKey = Common.STATUS + one.getType()+ Common.HENG  + one.getIp() + Common.HENG +one.getStatus();
        Set<String> redisKeyByPattern = getRedisKeyByPattern(oldKey);
        log.info("节点未上线  则通知上线 {}",one);
        updateById(one);
        redisKeyByPattern.stream().forEach(oldKey_val -> {
            //更新redis
            log.info("注意 【】 节点未上线  更新redis 则通知上线 {}  {} {}",oldKey_val , newKey,one);
            redisTemplateStr.delete(oldKey_val);

            redisTemplateObj.opsForValue().setIfAbsent(newKey,JSONUtil.toJsonStr(one));
        });
        if (updateNextNode){
            //需要更新子节点  也就是 t-0-ip-mac/rfid-1类型的数据
            updayeNodeAndRedis(one,statusNext);
        }
    }

    @Override
    public void deviceUpdate(SysNodes sysNodes, Set<String> keysChild, Integer status) {
        //如果没有 从数据库查询
        List<SysNodes> nodesList =null;
        //先从redis查询子节点
        List<Object> objects = redisTemplateObj.opsForValue().multiGet(keysChild);
        if (CollectionUtils.isNotEmpty(objects)){
            nodesList=objects.stream().map(obj ->   JSON.parseObject((String) obj,SysNodes.class)  )
                    .collect(Collectors.toList());
        }else {
            //如果没有 从数据库查询
            nodesList =  this.list(new LambdaQueryWrapper<SysNodes>()
                    .gt(SysNodes::getType, Common.ONE)
                    .eq(SysNodes::getStatus,status)
                    .eq(ObjectUtils.isNotNull(sysNodes),SysNodes::getParentId,sysNodes.getId())
            );
        }
        nodesList.stream().forEach(terminalNode -> {
            String ip = terminalNode.getType() > 1 ? (terminalNode.getType()>2?terminalNode.getUserCode():terminalNode.getMac()) : terminalNode.getIp();
            //变更成离线状态
            terminalNode.setUpdateTime( LocalDateTime.now());
            terminalNode.setStatus(status==Common.ZERO?Common.ONE:Common.ZERO);
            NodesColorEnum nodesColorEnum = NodesColorEnum.find(terminalNode.getType());
            terminalNode.setColor(terminalNode.getStatus()==Common.ZERO?nodesColorEnum.getColor_no():nodesColorEnum.getColor());
            String oldKey = Common.NEXTNODE + terminalNode.getType()+ Common.HENG  + ip + Common.HENG +"*";
            String newKey = Common.NEXTNODE + terminalNode.getType()+ Common.HENG  + ip + Common.HENG +terminalNode.getStatus();
            boolean updateById = updateById(terminalNode);
            //更新终端设备的所有状态
            if (updateById){
                //更新redis
                log.info("  更新redis  {}  {} {}",oldKey , newKey,terminalNode);
                deleteByPattern(oldKey);
                redisTemplateObj.opsForValue().setIfAbsent(newKey,JSONUtil.toJsonStr(terminalNode));
            }
        });
    }

    /**
     * 模糊redis删除
     * @param pattern
     */
    public void deleteByPattern(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }
    /**
     * 根据类信息存储到redis
     * @param entity
     */
    private void getRedisIp(SysNodes entity) {
        String ip = entity.getType() > 1 ? (entity.getType()>2?entity.getUserCode():entity.getMac()) : entity.getIp();
        redisTemplateObj.opsForValue().setIfAbsent(Common.AP+entity.getType()+Common.HENG+ip, JSONUtil.toJsonStr(entity));
        redisTemplateObj.opsForValue().setIfAbsent(Common.STATUS+entity.getType()+Common.HENG+ip+Common.HENG+entity.getStatus(),JSONUtil.toJsonStr(entity));
    }

    private void checkUpParam(SysNodes entity) {
        if (entity.getType().intValue()> Common.ONE || StringUtils.isBlank(entity.getIp())){
            throw new BusinessException(Common.FORBID_ADD_RFID);
        }
        if (entity.getType().intValue()==Common.ZERO){
            //查询本部门下MPP的个数
            if (this.count(new LambdaQueryWrapper<SysNodes>().eq(SysNodes::getType, Common.ZERO).notIn(SysNodes::getIp,entity.getIp())
                    .eq(SysNodes::getDeptId, entity.getDeptId()))==1){
                throw new BusinessException(Common.FORBID_EXIT_TWO);
            }
        }
    }

    /**
     * 更新节点
     *
     * @param id   节点ID
     * @param formData 节点表单对象
     * @return 是否修改成功
     */
    @Override
    public boolean updateSysNodes(Long id,SysNodesForm formData) {
        SysNodes entity = sysNodesConverter.toEntity(formData);
        NodesEnum nodesEnum = NodesEnum.find(entity.getType());
        NodesColorEnum nodesColorEnum = NodesColorEnum.find(entity.getType());
        entity.setId(id);
        entity.setSymbolSize(nodesEnum.getVal());
        entity.setColor(nodesColorEnum.getColor());
        checkUpParam(entity);
        boolean updateById = false;
        try {
            updateById = this.updateById(entity);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (updateById){
            String ip = entity.getType() > 1 ? (entity.getType()>2?entity.getUserCode():entity.getMac()) : entity.getIp();
            redisTemplateObj.opsForValue().setIfPresent(Common.AP+entity.getType()+Common.HENG+ip,JSONUtil.toJsonStr(entity));
            redisTemplateObj.opsForValue().setIfPresent(Common.STATUS+entity.getType()+Common.HENG+ip+Common.HENG+entity.getStatus(),JSONUtil.toJsonStr(entity));

        }
        return updateById;
    }
    
    /**
     * 删除节点
     *
     * @param ids 节点ID，多个以英文逗号(,)分割
     * @return 是否删除成功
     */
    @Override
    public boolean deleteSysNodess(String ids) {
        Assert.isTrue(StrUtil.isNotBlank(ids), "删除的节点数据为空");
        // 逻辑删除
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(Long::parseLong)
                .toList();
        List<SysNodes> nodesList = this.listByIds(idList);

        if (  CollectionUtils.isNotEmpty(nodesList)){
            nodesList.stream().forEach(entity ->{
                if (Common.nodeQueue.containsKey(entity.getIp())){
                   Common.nodeQueue.remove(entity.getIp());
                }
                if (entity.getType()<2){
                    String ip = entity.getType() > 1 ? (entity.getType()>2?entity.getUserCode():entity.getMac()) : entity.getIp();
                    redisTemplate.delete(Common.AP+entity.getType()+Common.HENG+ip );
                    String pattern = Common.STATUS + entity.getType() + Common.HENG + ip + Common.HENG + "*";
                    Set<String> keys = redisTemplate.keys(pattern);
                    if (keys != null && !keys.isEmpty()) {
                        redisTemplate.delete(keys);
                    }
                    //删除路由信息
                    redisTemplate.delete(Common.ROUTE+ip);
                    //删除路由关联表
                    sysNodeRouteService.getBaseMapper().delete(new LambdaQueryWrapper<SysNodeRoute>()
                            .eq(SysNodeRoute::getNodeId,entity.getId()));
                }else{
                    SysNodes sysNodes = this.getBaseMapper().selectById(entity.getParentId());
                    if (ObjectUtils.isNotNull(sysNodes)){
                        //删除的是终端
                        String pattern = Common.NEXTNODE+ sysNodes.getType() + Common.HENG
                                + sysNodes.getIp()+ Common.HENG +(entity.getType() > Common.TWO ?entity.getUserCode():entity.getMac())
                                + "*" ;
                        Set<String> keys = redisTemplate.keys(pattern);
                        if (keys != null && !keys.isEmpty()) {
                            redisTemplateObj.delete(keys);
                        }
                    }
                }
            });
        }
        boolean removeByIds = this.removeByIds(idList);
        return removeByIds;
    }

    /**
     * 获取节点列表
     * @return {@link List<Option<String>>} 节点选项列表
     */
    @Override
    public List<Option<String>> listNodesOptions() {
        List<SysNodes> list =  getSysNodes();
        return sysNodesConverter.toOptions(list);
    }

    /**
     * 获取用户当前的组织
     * @return
     */
    @Override
    public List<Long> getSysNodesDept() {
        List<Long> list = new ArrayList<>();
        Integer dataScope = SecurityUtils.getDataScope();
        DataScopeEnum dataScopeEnum = IBaseEnum.getEnumByValue(dataScope, DataScopeEnum.class);
        switch (dataScopeEnum) {
            case ALL:
                break;
            case DEPT:
                list.add(SecurityUtils.getDeptId());
                break;
            case SELF:
                list.add(SecurityUtils.getUserId());
                break;
            // 默认部门及子部门数据权限
            default:
                Long deptId = SecurityUtils.getDeptId();
                List<Dept> depots = deptService.getBaseMapper().selectList(new LambdaQueryWrapper<Dept>().eq(Dept::getId, deptId).or().apply(" OR FIND_IN_SET({0} , tree_path)", deptId));
                list = depots.stream().map(Dept::getId).collect(Collectors.toList());
                break;
        }
        return list;
    }

    @Override
    public GraphNodeVO graphOptions() {
        //获取节点信息
        List<SysNodesVO> list =  caculationLation();
        GraphNodeVO build = GraphNodeVO.builder().build();
        List<LinkVO> linkVOS = new ArrayList<>();
        List<NodesVO> nodesVOS = new ArrayList<>();
        list.stream().forEach(sysNodes -> {
            NodesVO nodeOption = sysNodesConverter.toNodeOptionBySysNodesVO(sysNodes);
            nodeOption.setItemStyle(ItemStyleVO.builder().color(sysNodes.getColor()).build());
            switch (sysNodes.getType()){
                case 2:
                    nodeOption.setName(NodesEnum.find(sysNodes.getType()).getTypeName()+""+(sysNodes.getHostName()!=null?sysNodes.getHostName():(sysNodes.getDhcpIp()!=null?sysNodes.getDhcpIp():sysNodes.getMac())));
                    break;
                case 3:
                    nodeOption.setName(NodesEnum.find(sysNodes.getType()).getTypeName()+""+(sysNodes.getRfidName()!=null?sysNodes.getRfidName():sysNodes.getUserCode()));
                    break;
                default:
                    nodeOption.setName(NodesEnum.find(sysNodes.getType()).getTypeName()+"-"+sysNodes.getLocation()+"-"+sysNodes.getIp());
                    break;
            }
            nodesVOS.add( nodeOption);
            linkVOS.add(LinkVO.builder().source(sysNodes.getParentId()).target(sysNodes.getId()).build());
        });
        build.setLink(linkVOS);
        build.setNodeList(nodesVOS);
        return build;
    }

    /**
     * 画布的宽度
     */
    private static final int CANVAS_WIDTH = 1000;
    /**
     * 画布的高度
     */
    private static final int CANVAS_HEIGHT = 1400;
    private static final int TOP_START_Y = 100;
    /**
     * 节点之间距离
     */
    private static final int MARGIN_LEFT = 200;

    /**
     * 自动计算节点位置并渲染
     * @return
     */
    public  List<SysNodesVO>    caculationLation(){
        //获取当前用户符合部门条件的所有节点
//        List<Long> collect =getSysNodesDept();
//        List<SysNodes> list = this.list(new LambdaQueryWrapper<SysNodes>()
//                .in(SysNodes::getDeptId, collect)
//                .eq(SysNodes::getStatus,Common.ONE)
//                .orderByAsc(SysNodes::getType).orderByAsc(SysNodes::getParentId)
//        );
        //获取MAP  MPP节点  获取终端的在线节点
        List<SysNodesVO> list = this.baseMapper.getSysNodesBy(SysNodesQuery.builder().build());
//        判断当前用户是不是管理员 如果
//        if(SecurityUtils.isAdmin()){
//            //是超级管理员 可以返回所有的管理员下的设备 主节点存在多个
//        }else if(SecurityUtils.isAdminLow()){
//            //是普通管理员 可以返回 管理员下的设备 主节点存在1个
//            list = this.baseMapper.getSysNodesBy(SysNodesQuery.builder().build());
//        }
        //获取MAP  MPP节点  获取终端的在线节点
        list = list.stream().filter(sysNodesVO -> !(sysNodesVO.getType().intValue() > Common.ONE && sysNodesVO.getStatus().intValue() == Common.ZERO) ).collect(Collectors.toList());
        Map<Integer, List<SysNodesVO>> treeList = list.stream() .collect(Collectors.groupingBy(SysNodesVO::getLevel));
        AtomicInteger sizeTop = new AtomicInteger();
        AtomicInteger sizeMid = new AtomicInteger();
        AtomicInteger sizeEnd = new AtomicInteger();
         treeList.forEach((type, nodeList) -> {
            switch (type){
                case 0:
                    sizeTop.set(treeList.get(0).size());
                    break;
                case 1:
                    sizeMid.set(treeList.get(1).size() );
                break;
                case 2:
                    sizeEnd.set( treeList.get(2).size() );
                break;
            }
        });
        AtomicInteger k= new AtomicInteger();
        AtomicInteger i= new AtomicInteger();
        AtomicInteger j= new AtomicInteger();
        list.stream().forEach(sysNodes -> {
            sysNodes.setY(CANVAS_HEIGHT/3+(CANVAS_HEIGHT/3)*sysNodes.getLevel()*2);
            if (sysNodes.getLevel()==Common.ZERO){
//                int i1 = CANVAS_WIDTH / (sizeTop.get() == 1 ? sizeTop.get() + 1 : sizeTop.get()*2);
//                sysNodes.setX(i1);
                sysNodes.setX((CANVAS_WIDTH/(sizeTop.get()==1?sizeTop.get()+1:sizeTop.get()))*k.incrementAndGet()*2);
            }else  if (sysNodes.getLevel()==Common.ONE){
                sysNodes.setX((CANVAS_WIDTH/(sizeMid.get()==1?sizeMid.get()+1:sizeMid.get()))*i.incrementAndGet()*2);
            }else   {
                sysNodes.setX(CANVAS_WIDTH/((sizeEnd.get()==1?sizeEnd.get()+1:sizeEnd.get()))*j.incrementAndGet()*2);
            }

        });
        return list;
    }


    /**
     * 节点请求上线
     *
     * @param ip      IP地址
     * @param type    类型
     * @param mac     MAC地址
     * @param routeIp 路由网
     * @return 返回成功或者失败
     */
    @Override
    public boolean addNodeUp(String ip, Integer type, String mac, String routeIp) {
        log.info("节点上线接口 {}  {} {} {}",routeIp,ip,type,mac);
        try {
            SysRoute sysRoute =null;
            //判断redis存不存在路由
            if (!redisTemplate.hasKey(Common.ROUTE+ip)){
                //如果没有说明库里面也没有 需要添加路由和节点的信息
                //先查询有无指定的路由  如果没有返回false
                sysRoute = sysRouteService.getOne(new LambdaQueryWrapper<SysRoute>().eq(SysRoute::getRouteIp, routeIp));
                if (ObjectUtils.isNull(sysRoute)){
                    return false;
                }
                redisTemplate.opsForValue().set(Common.ROUTE+ip, JSONUtil.toJsonStr(sysRoute));
            }else{ 
                //如果有说明数据库已经存在路由了
                sysRoute = extracted(ip);
            }
            //先去redis查询是否存在IP
            String oldKey = Common.STATUS + type+ Common.HENG  + ip + Common.HENG +"*";
            String newKey = Common.STATUS +type+ Common.HENG  +ip + Common.HENG +Common.ONE_STR;
            Set<String> redisKeyByPattern = getRedisKeyByPattern(oldKey);
            for (String oldKeyVal :redisKeyByPattern) {
                String statusStr = oldKeyVal.substring(oldKeyVal.lastIndexOf("-") + 1);
                SysNodes one=null;
                //设备是离线的需修改  设备是有线的无需要修改
                if (!redisTemplateObj.hasKey(oldKeyVal)){
                    //不存在去数据库查
                    one = this.getOne(new LambdaQueryWrapper<SysNodes>() .eq(SysNodes::getType, type).eq(SysNodes::getIp, ip));
                }else{
                    one=JSON.parseObject((String)redisTemplateObj.opsForValue().get(oldKeyVal),SysNodes.class);
                }
                if (statusStr.equals(Common.ZERO_STR)){
                    if (ObjectUtils.isNotNull(one)){
                        //判断是否存在对应关系
                        long count = sysNodeRouteService.count(new LambdaQueryWrapper<SysNodeRoute>()
                                .eq(SysNodeRoute::getNodeId, one.getId())
                                .eq(SysNodeRoute::getRouteId, sysRoute.getId())
                        );
                        //保存关系表
                        if (count==0){
                            log.info("添加路由关联 {} {}",one.getId(), sysRoute.getId());
                            sysNodeRouteService.saveSysNodeRoute(SysNodeRouteForm.builder()
                                    .routeId(sysRoute.getId()).nodeId(one.getId()).build());
                        }
                        log.info("节点上线接口 请求成功{}", one);
                        if (one.getPromise()==Common.ONE){
                            return false;
                        }
                        if (!Common. nodeQueue.containsKey(one.getIp())){
                            Common. nodeQueue.put(one.getIp(),one);
                        }
                        one.setStatus(Common.ONE);
                        one.setColor(NodesColorEnum.find(one.getType()).getColor());
                        one.setMac(mac);
                        //一次节点请求上线就是一次重新上线
                        one.setCreateTime( LocalDateTime.now());
                        boolean updateById = updateById(one);
                        if (updateById){
                            redisTemplateObj.delete(oldKeyVal);
                            redisTemplateObj.opsForValue().setIfAbsent(newKey,JSONUtil.toJsonStr(one));
                        }
                        return true;
                    }
                }else  if (statusStr.equals(Common.ONE_STR)){
                    if (one.getPromise()==Common.ONE){
                        return false;
                    }
                    if (!Common. nodeQueue.containsKey(one.getIp())){
                        Common. nodeQueue.put(one.getIp(),one);
                    }
                    return true;
                }
            };
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return false;
    }

    @Override
    public void saveTerminalNode( NodesThirdVO  nodesThirdVO, SysNodes sysNodes) {
        if (ObjectUtils.isNotNull(nodesThirdVO) ) {
            String newKey = Common.NEXTNODE+ sysNodes.getType() + Common.HENG
                    + sysNodes.getIp()+ Common.HENG +(nodesThirdVO.getType() == Common.ZERO ?nodesThirdVO.getMac():nodesThirdVO.getRfid())
                    + Common.HENG+Common.ONE_STR ;
            SysNodes build = SysNodes.builder() .type(nodesThirdVO.getType() == Common.ZERO ? NodesColorEnum.END.getMode() : NodesColorEnum.RFID.getMode())
                    .status(Common.ONE).color(NodesColorEnum.END.getColor()).userCode(nodesThirdVO.getRfid())
                    .mac(nodesThirdVO.getMac()).deptId(sysNodes.getDeptId()).level(sysNodes.getType()+1)
                    .symbolSize(nodesThirdVO.getType() == Common.ZERO ? NodesEnum.END.getVal() : NodesEnum.RFID.getVal()).promise(Common.ZERO)
                    .parentId(sysNodes.getId())
                    .build();
            log.info("终端新增 {}", build);
            boolean save = this.save(build);
            if (save){
                redisTemplateObj.opsForValue().setIfAbsent(newKey,JSONUtil.toJsonStr(build));
            }
        }
    }

    @Override
    public void updateTerminalNode(NodesThirdVO nodesThirdVO, SysNodes sysNodes, Integer status) {
        String oldKey =null;
        if (ObjectUtils.isNull(nodesThirdVO)){
              oldKey = Common.NEXTNODE+ sysNodes.getType() + Common.HENG
                    + sysNodes.getIp()+ Common.HENG + "*" ;
        }else {
            oldKey = Common.NEXTNODE + sysNodes.getType() + Common.HENG
                    + sysNodes.getIp() + Common.HENG + (nodesThirdVO.getType() == Common.ZERO ? nodesThirdVO.getMac() : nodesThirdVO.getRfid())
                    + "*";
        }
            Set<String> keys = redisTemplateStr.keys(oldKey);
            keys.stream().forEach(s -> {
                String statusStr = s.substring(s.lastIndexOf(Common.HENG) + 1);
                if (ObjectUtils.isNull(nodesThirdVO)){
                    Integer statusNew;
                    statusNew=status==1?Common.ZERO:status;
                    if (Integer.parseInt(statusStr)!=statusNew){
                        updateNodeTermial(null, sysNodes, s, statusNew);
                    }
                }else{
                    if (Integer.parseInt(statusStr)!=status){
                        updateNodeTermial(nodesThirdVO, sysNodes, s, status);
                    }
                }

            });
    }


    @Override
    public void updateNodeTermial(NodesThirdVO nodesThirdVO, SysNodes sysNodes, String key, Integer statusNew) {
        Object o =redisTemplateObj.opsForValue().get(key);
        SysNodes parsedObject = JSON.parseObject((String) o, SysNodes.class);
        //            Integer statusNew  = status == 0 ? Common.ONE : Common.ZERO;
        //如果等于0  需要更新redis和数据库
        String newKey = Common.NEXTNODE+ sysNodes.getType() + Common.HENG
                + sysNodes.getIp()+ Common.HENG +(parsedObject.getType() > Common.TWO ?parsedObject.getUserCode(): parsedObject.getMac())
                + Common.HENG + statusNew;
        parsedObject.setStatus(statusNew);
        NodesColorEnum nodesColorEnum = NodesColorEnum.find(parsedObject.getType());
        parsedObject.setColor(parsedObject.getStatus()==0?nodesColorEnum.getColor_no():nodesColorEnum.getColor());
        parsedObject.setCreateTime(LocalDateTime.now());
        boolean updateById = updateById(parsedObject);
        if (updateById){
            redisTemplateObj.delete(key);
            redisTemplateObj.opsForValue().setIfAbsent( newKey,JSONUtil.toJsonStr(parsedObject));
        }
    }

    /**
     * 重启节点信息
     *
     * @param ip
     * @param type
     * @return 返回成功或者失败
     */
    @Override
    public boolean reboot(String ip, Integer type) {
        SysRoute sysRoute = extracted(ip);
        Result<NodesThirdVO> reboot = null;
        try {

            FeignService feignClient = feignClientFactory.getFeignClient(pingUrl(sysRoute));
            log.info("三方接口存在 4 {} {}",ip,feignClient);
            if (ObjectUtils.isNotNull(feignClient)){
                reboot = feignClient.reboot(ip,type);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (reboot.getCode().equals(Common.RES_TRUE_STR)){
            Common.nodeQueue.remove(ip);
            this.update(new LambdaUpdateWrapper<SysNodes>().eq(SysNodes::getIp,ip).set(SysNodes::getStatus, Common.ZERO)
                    .set(SysNodes::getColor, NodesColorEnum.find(Common.ZERO).getColor_no()));
            //子节点同时下线
            SysNodes one = this.getOne(new LambdaQueryWrapper<SysNodes>().eq(SysNodes::getIp,ip));
            if (ObjectUtils.isNotNull(one)){
                this.update(new LambdaUpdateWrapper<SysNodes>().eq(SysNodes::getParentId,one.getId()).gt(SysNodes::getType,type)
                        .set(SysNodes::getStatus, Common.ZERO).set(SysNodes::getUpdateTime, LocalDateTime.now())
                        .set(SysNodes::getColor, NodesColorEnum.find(Common.ZERO).getColor_no()));
            }
            return true;
        }
        return false;
    }

    /**
     * 拼url
     * @param sysRoute
     * @return
     */
    private static String pingUrl(SysRoute sysRoute) {
        if (ObjectUtils.isNull(sysRoute)){
            return null;
        }
        return Common.HTTP + sysRoute.getRouteIp() + ":" + sysRoute.getPort();
    }

    /**
     * 根据IP获取Redis路由
     * @param ip
     * @return
     */
    private SysRoute extracted(String ip) {
        if (!redisTemplate.hasKey(Common.ROUTE + ip)){
            return null;
        }
        String strRoute = redisTemplate.opsForValue().get(Common.ROUTE + ip);
        JSONObject jsonObject = JSON.parseObject(strRoute);
        //如果有说明数据库已经存在路由了
        SysRoute javaObject = JSON.toJavaObject(jsonObject, SysRoute.class);
        return javaObject;
    }

    @Override
    public List<GraphNodeVO> getAllNodes() {
        List<SysNodes> list = getSysNodes();
        return sysNodesConverter.toGraphNodeVOEnty(list);
    }

    /**
     * 获取当前用户的 MAP MPP节点
     * @return
     */
    private List<SysNodes> getSysNodes() {
        List<SysNodes> list = this.baseMapper.listSysNodes(SysNodesQuery.builder().typeNo(Common.TWO).build());
//        List<SysNodes> list = this.list(new LambdaQueryWrapper<SysNodes>().in(SysNodes::getDeptId, collect)
//                .eq(SysNodes::getType, Common.ONE)
//                .or().eq(SysNodes::getType, Common.ZERO)
//        );
        return list;
    }

    @Override
    public SysNodes getSysNodesByType(Integer type) {
        List<SysNodes> list = this.baseMapper.listSysNodes(SysNodesQuery.builder().type(Common.ZERO).build());
//        SysNodesQuery queryParams=new SysNodesQuery();
//        System.out.println(SecurityUtils.isAdmin());
//        if (SecurityUtils.isAdmin()){
//            //查找属于这个账号的管理员的部门
//            List<SysNodes> list = this.list(new LambdaQueryWrapper<SysNodes>()
//
//                    .eq(SysNodes::getType, Common.ZERO).eq(SysNodes::getDeptId, SecurityUtils.getDeptId()));
//            return (CollectionUtils.isNotEmpty(list)) ?list.get(0):null;
//        }else{
//            LambdaQueryWrapper<SysNodes> nodeWrapper = new LambdaQueryWrapper<SysNodes>()
//
//                    .eq(SysNodes::getType, Common.ZERO)
//                    .eq(SysNodes::getDeptId, SecurityUtils.getDeptId()) ;
//            String[] split = SecurityUtils.TreePath().split(",");
//            Arrays.stream(split).forEach(s -> nodeWrapper.or().eq(SysNodes::getDeptId, s));
//            //查找属于这个账号的管理员的部门
//            List<SysNodes> list = this.list(nodeWrapper);
//            return (CollectionUtils.isNotEmpty(list)) ?list.get(0):null;
//        }
        return (CollectionUtils.isNotEmpty(list)) ?list.get(0):null;
    }

    @Override
    public  Result  getConfig(String ip, String cfgItem, Integer type) {
        FeignService feignService = feignClientFactory.getFeignClient(pingUrl( extracted(ip)));
        log.info("三方接口存在 5 {} {}",ip);
        if (ObjectUtils.isNull(feignService)){
            throw new BusinessException("接口服务掉线");
        }
        return feignService.getConfig(ip, cfgItem, type);

//        if (config.getCode().equals(Common.RES_TRUE_STR)){
//            //Gson gson = new Gson();
//          //  Map<String, HashMap<String, String>> objectMapper = gson.fromJson(JSONUtils.toJSONString(config.getData()), new TypeToken< Map<String,  Map<String, String>>>(){}.getType());
////             Map<String, String> parse =( Map<String, String>) JSONUtils.parse(data);
//            System.out.println();
////            Map<String, String> data = config.getData();
//            if (ObjectUtils.isNotNull(config.getData())){
//                return config.getData();
//            }
//        }
//        return null;
    }

    @Override
    public boolean updateSet(NodesThirdVO nodesThirdVO) {

        FeignService feignClient = feignClientFactory.getFeignClient(pingUrl( extracted(nodesThirdVO.getIp())));
        log.info("三方接口存在 w {} {}",nodesThirdVO.getIp(),feignClient);
        if (ObjectUtils.isNull(feignClient)){
             throw new BusinessException("接口服务掉线");
        }
        Result  config = feignClient.updateSet(nodesThirdVO.getIp()
                ,nodesThirdVO.getCfg_item(),nodesThirdVO.getType(),nodesThirdVO.getJsonData());
        if (config.getCode().equals(Common.RES_TRUE_STR)){
            return true;
        }
        return false;
    }

    @Override
    public List<Option<Long>> listNodeOptions(boolean onlyParent) {
        List<SysNodes> nodesList = this.list(new LambdaQueryWrapper<SysNodes>()
                .le(!onlyParent, SysNodes::getType,  Common.ONE)
                .orderByAsc(SysNodes::getType)
        );
        return buildNodeOptions(SystemConstants.ROOT_NODE_ID, nodesList);
    }

    /**
     * 递归生成菜单下拉层级列表
     *
     * @param parentId 父级ID
     * @param nodesList 菜单列表
     * @return 菜单下拉列表
     */
    private List<Option<Long>> buildNodeOptions(Long parentId, List<SysNodes> nodesList) {
        List<Option<Long>> nodeOptions = new ArrayList<>();
        for (SysNodes sysNodes : nodesList) {
            if (sysNodes.getParentId().equals(parentId)) {
                Option<Long> option = new Option<>(sysNodes.getId(), sysNodes.getIp());
                List<Option<Long>> subMenuOptions = buildNodeOptions(sysNodes.getId(), nodesList);
                if (!subMenuOptions.isEmpty()) {
                    option.setChildren(subMenuOptions);
                }
                nodeOptions.add(option);
            }
        }
        return nodeOptions;
    }

    @Override
    public List<SysNodes> getByRouteId(Long routeId) {
        return this.baseMapper.getByRouteId(Collections.singletonList(routeId)) ;
    }
}
